package com.zc.manager.device.service.impl;

import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.zc.common.core.foreignkeyservice.AssociatedService;
import com.zc.common.core.foreignkeyservice.ForeignKeyService;
import com.zc.common.core.foreignkeyservice.LinkModel;
import com.zc.core.device.DeviceRegistry;
import com.zc.manager.device.domain.IotCategory;
import com.zc.manager.device.domain.IotProduct;
import com.zc.manager.device.domain.IotProtocol;
import com.zc.manager.device.events.DeviceProductDeployEvent;
import com.zc.manager.device.mapper.IotCategoryMapper;
import com.zc.manager.device.mapper.IotProductMapper;
import com.zc.manager.device.mapper.IotProtocolMapper;
import com.zc.manager.device.service.IIotProductService;
import com.zc.web.bean.FastBeanCopier;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.List;

/**
 * 产品Service业务层处理
 *
 * @author xiepufeng
 * @date 2022-05-31
 */
@Service
public class IotProductServiceImpl implements IIotProductService, ForeignKeyService, AssociatedService {

    @Resource
    private IotProductMapper iotProductMapper;

    @Resource
    private IotCategoryMapper iotCategoryMapper;

    @Resource
    private IotProtocolMapper iotProtocolMapper;

    @Resource
    private DeviceRegistry registry;

    @Resource
    private ApplicationEventPublisher eventPublisher;

    @Resource(name="iotCategoryServiceImpl")
    private AssociatedService associatedService;

    @PostConstruct
    public void init()
    {
        // 注册外键
        registerForeignKey();
    }


    /**
     * 查询产品
     *
     * @param id 产品主键
     * @return 产品
     */
    @Override
    public IotProduct selectIotProductById(String id)
    {
        return iotProductMapper.selectIotProductById(id);
    }

    /**
     * 根据id批量查询
     *
     * @param id 产品主键
     * @return
     */
    @Override
    public List<IotProduct> selectIotProductByIds(String[] id) {
        return iotProductMapper.selectIotProductByIds(id);
    }


    /**
     * 查询产品列表
     *
     * @param iotProduct 产品
     * @return 产品
     */
    @Override
    public List<IotProduct> selectIotProductList(IotProduct iotProduct)
    {
        return iotProductMapper.selectIotProductList(iotProduct);
    }

    /**
     * 新增产品
     *
     * @param iotProduct 产品
     * @return 结果
     */
    @Override
    public int insertIotProduct(IotProduct iotProduct)
    {

        // 1. 验证产品ID是否重复
        // 1.1 获取产品ID数据
        String id = iotProduct.getId();

        if (StringUtils.isEmpty(id)) {
            throw new ServiceException("产品ID不存在", HttpStatus.BAD_REQUEST);
        }

        // 1.2 根据产品ID查询产品数据
        IotProduct iotProduct1 = iotProductMapper.selectIotProductById(id);

        if (iotProduct1 != null)
        {
            // 1.3 根据产品ID查询出产品数据，产品ID已存在
            throw new ServiceException("产品ID不能重复", HttpStatus.BAD_REQUEST);
        }

        // 2. 查询品类是否存在
        // 2.1 获取品类id
        Long categoryId = iotProduct.getCategoryId();

        // 2.2 根据品类id查询品类数据
        IotCategory iotCategory = iotCategoryMapper.selectIotCategoryById(categoryId);

        if (iotCategory == null)
        {
            // 2.3 根据品类id查询不到品类信息
            throw new ServiceException("所属品类不存在", HttpStatus.BAD_REQUEST);
        }

        // 3. 查询消息协议是否存在
        String protocolId = iotProduct.getProtocolId();

        if (!"jetlinks.v1.0".equals(protocolId)) {
            IotProtocol iotProtocol = iotProtocolMapper.selectIotProtocolById(protocolId);

            if (iotProtocol == null)
            {
                throw new ServiceException("所属协议不存在", HttpStatus.BAD_REQUEST);
            }
        }

        iotProduct.setCreateTime(DateUtils.getNowDate());
        return iotProductMapper.insertIotProduct(iotProduct);
    }

    /**
     * 修改产品
     *
     * @param iotProduct 产品
     * @return 结果
     */
    @Override
    public int updateIotProduct(IotProduct iotProduct)
    {
        // 1. 主键不能为空
        String id = iotProduct.getId();

        if (id == null)
        {
            throw new ServiceException("产品主键不能为空", HttpStatus.BAD_REQUEST);
        }

        // 2. 查询品类是否存在
        // 2.1 获取品类id
        Long categoryId = iotProduct.getCategoryId();

        // 2.2 根据品类id查询品类数据
        IotCategory iotCategory = iotCategoryMapper.selectIotCategoryById(categoryId);

        if (iotCategory == null)
        {
            // 2.3 根据品类id查询不到品类信息
            throw new ServiceException("所属品类不存在", HttpStatus.BAD_REQUEST);
        }

        // 3. 查询消息协议是否存在
        String protocolId = iotProduct.getProtocolId();

        if (!"jetlinks.v1.0".equals(protocolId)) {
            IotProtocol iotProtocol = iotProtocolMapper.selectIotProtocolById(protocolId);

            if (iotProtocol == null)
            {
                throw new ServiceException("所属协议不存在", HttpStatus.BAD_REQUEST);
            }
        }

        iotProduct.setUpdateTime(DateUtils.getNowDate());
        // 默认为未发布
        iotProduct.setPublishState(IotProduct.UN_PUBLISH);
        return iotProductMapper.updateIotProduct(iotProduct);
    }

    /**
     * 批量删除产品
     *
     * @param ids 需要删除的产品主键
     * @return 结果
     */
    @Override
    public int deleteIotProductByIds(String[] ids)
    {

        for (String id : ids) {
            // 检查产品是否存在
            IotProduct iotProduct = iotProductMapper.selectIotProductById(id);

            if (iotProduct == null)
            {
                // 抛异常返回前端关联信息
                throw new ServiceException("参数错误", HttpStatus.NOT_FOUND);
            }

            // 检查产品是否已经发布（不允许删除已发布的产品）
            if (IotProduct.PUBLISH == iotProduct.getPublishState()) {
                // 抛异常返回前端关联信息
                throw new ServiceException("不允许删除已经发布的产品", HttpStatus.CONFLICT);
            }
        }

        // 有关联数据则不允许删除

        // 检擦是否有数据关联
        LinkModel linkModel = getWhoReferenceMe(ids);

        // 不为空则有数据关联
        if (linkModel != null)
        {
            // 抛异常返回前端关联信息
            throw new ServiceException(linkModel.getDescription(), HttpStatus.CONFLICT);
        }

        return iotProductMapper.deleteIotProductByIds(ids);
    }

    /**
     * 删除产品信息
     *
     * @param id 产品主键
     * @return 结果
     */
    @Override
    public int deleteIotProductById(String id)
    {

        // 检查产品是否存在
        IotProduct iotProduct = iotProductMapper.selectIotProductById(id);

        if (iotProduct == null)
        {
            // 抛异常返回前端关联信息
            throw new ServiceException("参数错误", HttpStatus.NOT_FOUND);
        }

        // 检查产品是否已经发布（不允许删除已发布的产品）
        if (IotProduct.PUBLISH == iotProduct.getPublishState()) {
            // 抛异常返回前端关联信息
            throw new ServiceException("不允许删除已经发布的产品", HttpStatus.CONFLICT);
        }

        // 有关联数据则不允许删除

        // 检擦是否有数据关联
        LinkModel linkModel = getWhoReferenceMe(id);

        // 不为空则有数据关联
        if (linkModel != null)
        {
            // 抛异常返回前端关联信息
            throw new ServiceException(linkModel.getDescription(), HttpStatus.CONFLICT);
        }


        return iotProductMapper.deleteIotProductById(id);
    }

    /**
     * 发布产品
     *
     * @param id 产品主键
     * @return 结果
     */
    @Override
    public int publishProduct(String id) {

        // 根据id获取协议信息
        IotProduct iotProduct = iotProductMapper.selectIotProductById(id);

        if (iotProduct == null) {
            throw new ServiceException("无法获取当前产品信息", HttpStatus.NOT_FOUND);
        }

        Mono.just(iotProduct)
                .flatMap(product -> registry
                .register(product.toProductInfo())
                .flatMap(i -> FastBeanCopier
                        .copy(product, new DeviceProductDeployEvent())
                        .publish(eventPublisher)))
                .subscribe();

        // 设置状态为已发布
        iotProduct.setPublishState(IotProduct.PUBLISH);
        return iotProductMapper.updateIotProduct(iotProduct);
    }

    /**
     * 取消发布产品
     *
     * @param id 产品主键
     * @return 结果
     */
    @Override
    public int unPublishProduct(String id) {

        // 根据id获取协议信息
        IotProduct iotProduct = iotProductMapper.selectIotProductById(id);

        if (iotProduct == null) {
            throw new ServiceException("无法获取当前产品信息", HttpStatus.NOT_FOUND);
        }

        // 设置状态为未发布
        iotProduct.setPublishState(IotProduct.UN_PUBLISH);

        int result = iotProductMapper.updateIotProduct(iotProduct);

        if (result > 0) {
            registry.unregisterProduct(id).subscribe();
        }

        return result;
    }

    /**
     * 更新产品其他配置
     *
     * @param id            产品id
     * @param configuration 其他配置
     * @return 更新结果
     */
    @Override
    public int updateConfiguration(String id, String configuration) {

        // 根据id获取协议信息
        IotProduct iotProduct = iotProductMapper.selectIotProductById(id);

        if (iotProduct == null) {
            throw new ServiceException("无法获取当前产品信息", HttpStatus.NOT_FOUND);
        }

        iotProduct.setConfiguration(configuration);

        return iotProductMapper.updateIotProduct(iotProduct);
    }

    /**
     * 更新协议支持
     *
     * @param id              产品id
     * @param protocolStandby 协议支持
     * @return 更新结果
     */
    @Override
    public int updateProtocolStandby(String id, String protocolStandby) {

        // 1. 主键不能为空
        if (id == null)
        {
            throw new ServiceException("产品主键不能为空", HttpStatus.BAD_REQUEST);
        }

        // 根据id获取协议信息
        IotProduct iotProduct = iotProductMapper.selectIotProductById(id);

        if (iotProduct == null) {
            throw new ServiceException("无法获取当前产品信息", HttpStatus.NOT_FOUND);
        }

        iotProduct.setProtocolStandby(protocolStandby);

        return updateIotProduct(iotProduct);
    }

    /**
     * 查询产品数量
     *
     * @return 产品数数量
     */
    @Override
    public int productCount() {
        return iotProductMapper.productCount();
    }

    /**
     * 通过外键值查找本实体，如果查找到则说明本实体关联了外键对应的实体。
     *
     * @param foreignKey      ： 本实体外键属性
     * @param foreignKeyValue ： 本实体的外键值
     * @return
     */
    @Override
    public LinkModel findByForeignKey(String foreignKey, Object foreignKeyValue) {

        if (FOREIGN_KEY_CATEGORY_ID.equals(foreignKey)) {
            IotProduct iotProduct = new IotProduct();
            iotProduct.setCategoryId((Long)foreignKeyValue);
            List<IotProduct> iotProducts = iotProductMapper.selectIotProductList(iotProduct);

            if (iotProducts != null && !iotProducts.isEmpty()) {
               return new LinkModel("产品数据已关联");
            }
        }

        return null;
    }

    /**
     * 根据外键删除所有关联数据
     *
     * @param foreignKey      ： 本实体外键属性
     * @param foreignKeyValue ： 本实体的外键值
     * @return
     */
    @Override
    public boolean deleteAllByForeignKey(String foreignKey, Object foreignKeyValue) {
        return false;
    }

    /**
     * 外键标识
     */
    private static final String FOREIGN_KEY_CATEGORY_ID = "categoryId";

    /**
     * 如果实体存在外键关联，则向外该键所指的实体服务注册外键，从而建立关联链关系。
     */
    @Override
    public void registerForeignKey() {
        associatedService.registerForeignKey(this, FOREIGN_KEY_CATEGORY_ID);
    }
}
